package com.frank.activedatabus.Core;

import com.frank.activedatabus.dispatch.ThreadMode;
import ohos.aafwk.ability.ILifecycle;
import ohos.aafwk.ability.Lifecycle;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;

import java.util.HashMap;
import java.util.Map;

/**
 * @author : frank
 * @date : 2021/3/16 9:50
 */
public class LiveEventBusCore {

    private final Map<String, LiveEvent<Object>> bus;
    private final Map<CustomActiveData, CustomLifeCycle> lifeCycleMap = new HashMap<>();

    /**
     * 可配置的项
     */
    private final Config config = new Config();
    private boolean autoClear;
    private final Map<String, ObservableConfig> observableConfigs;

    private LiveEventBusCore() {
        bus = new HashMap<>();
        observableConfigs = new HashMap<>();
        autoClear = false;
    }

    private static class SingletonHolder {
        private static final LiveEventBusCore DATA_BUS = new LiveEventBusCore();
    }

    public static LiveEventBusCore get() {
        return SingletonHolder.DATA_BUS;
    }

    public <T> LiveEvent<T> getChannel(String target, Class<T> type) {
        if (!bus.containsKey(target)) {
            bus.put(target, new LiveEvent<>(target));
        }
        return (LiveEvent<T>) bus.get(target);
    }

    public LiveEvent<Object> getChannel(String target) {
        return getChannel(target, Object.class);
    }

    /**
     * use the class Config to set params
     * first of all, call config to get the Config instance
     * then, call the method of Config to config LiveEventBus
     * call this method in Application.onCreate
     */
    public Config config() {
        return config;
    }

    public ObservableConfig config(String key, ObservableConfig config) {
        if (!observableConfigs.containsKey(key)) {
            observableConfigs.put(key, config);
        }
        return observableConfigs.get(key);
    }

    public class LiveEvent<T> implements Observable<T> {

        private final String key;

        private CustomActiveData customActiveData;

        private EventHandler currentHandler = new EventHandler(EventRunner.current());

        private volatile ObservableConfig observableConfig;


        public LiveEvent(String key) {
            this.key = key;
            customActiveData = new CustomActiveData(key);
        }

        @Override
        public void post(T value) {
            customActiveData.setData(value);
        }

        @Override
        public void post(ILifecycle owner, T value) {
            currentHandler.postTask(new PostLifeValueTask(value, owner));
        }

        @Override
        public void postDelay(T value, long delay) {
            currentHandler.postTask(new PostValueTask(value), delay);
        }

        @Override
        public void postDelay(ILifecycle owner, T value, long delay) {
            currentHandler.postTask(new PostLifeValueTask(value, owner), delay);
        }

        @Override
        public void observe(ILifecycle owner, Observer<T> dataChangeListener) {
            customActiveData.observe(dataChangeListener, owner.getLifecycle(), isAlways(), isSticky(), getThreadMode());
        }

        @Override
        public void observeForever(Observer dataChangeListener) {
            customActiveData.observe(dataChangeListener, null, isAlways(), isSticky(), getThreadMode());
        }


        @Override
        public void removeObserver(Observer observer) {
            customActiveData.removeObserver(observer);
        }


        @Override
        public Observable<T> withBackground(boolean always) {
            getObservableConfig().isAlways = always;
            return this;
        }

        @Override
        public Observable<T> withSticky(boolean sticky) {
            getObservableConfig().isSticky = sticky;
            return this;
        }

        @Override
        public Observable<T> withThreadMode(ThreadMode threadMode) {
            getObservableConfig().setThreadMode(threadMode);
            return this;
        }

        ObservableConfig getObservableConfig() {
            if (observableConfig == null) {
                synchronized (this) {
                    if (observableConfig == null) {
                        observableConfig = new ObservableConfig();
                    }
                }
            }
            return observableConfig;
        }

        private boolean autoClear() {
            if (observableConfigs.containsKey(key)) {
                ObservableConfig config = observableConfigs.get(key);
                if (config.autoClear != null) {
                    return config.autoClear;
                }
            }
            return autoClear;
        }

        private boolean isAlways() {
            if (observableConfig == null) {
                return false;
            } else {
                return observableConfig.isAlways;
            }
        }

        private boolean isSticky() {
            if (observableConfig == null) {
                return false;
            } else {
                return observableConfig.isSticky;
            }
        }

        private ThreadMode getThreadMode() {
            if (observableConfig == null || observableConfig.getThreadMode() == null) {
                return ThreadMode.DEFAILT;
            } else {
                return observableConfig.getThreadMode();
            }
        }


        private void postInternal(T value) {
            customActiveData.setData(value);
        }

        private class PostValueTask implements Runnable {

            private Object newValue;

            public PostValueTask(Object newValue) {
                this.newValue = newValue;
            }

            @Override
            public void run() {
                postInternal((T) newValue);
            }
        }

        private class PostLifeValueTask implements Runnable {
            private Object newValue;
            private ILifecycle owner;

            public PostLifeValueTask(Object newValue, ILifecycle owner) {
                this.newValue = newValue;
                this.owner = owner;
            }

            @Override
            public void run() {
                if (owner != null) {
                    if (owner.getLifecycle().getLifecycleState().compareTo(Lifecycle.Event.ON_START) >= 0) {
                        postInternal((T) newValue);
                    }
                }
            }
        }
    }
}
